package create.rpc.client;


import create.rpc.RpcClient;
import create.rpc.entity.RpcRequest;
import create.rpc.entity.RpcResponse;
import create.rpc.util.RpcMessageChecker;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;


@Slf4j
public class RpcClientProxy implements InvocationHandler {

    private final RpcClient client;

    public RpcClientProxy(RpcClient client) {
        this.client = client;
    }


    /**
     * Proxy.newProxyInstance 方法用于创建动态代理类的实例。该方法接受三个参数：
     *
     * ClassLoader loader： 用于定义代理类的类加载器。
     * Class<?>[] interfaces： 代理类要实现的接口数组。
     * InvocationHandler h： 实现了 InvocationHandler 接口的对象，用于处理代理对象上的方法调用。
     *
     * 在动态代理中，当代理对象的方法被调用时，实际上会调用 InvocationHandler 中的 invoke 方法。
     * 因此，Proxy.newProxyInstance 方法要求传入一个实现了 InvocationHandler 接口的类的实例，以便在代理对象方法被调用时执行自定义的逻辑。
     *
     */
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz)
    {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class<?>[]{clazz},this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("调用方法: {}#{}", method.getDeclaringClass().getName(), method.getName());

        RpcRequest rpcRequest = new RpcRequest(UUID.randomUUID().toString(), method.getDeclaringClass().getName(),
                method.getName(), args, method.getParameterTypes(), false);
        RpcResponse rpcResponse = null;
        if (client instanceof NettyClient) {
            CompletableFuture<RpcResponse> completableFuture = (CompletableFuture<RpcResponse>) client.sendRequest(rpcRequest);
            try {
                rpcResponse = completableFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                log.error("方法调用请求发送失败", e);
                return null;
            }
        }
        RpcMessageChecker.check(rpcRequest, rpcResponse);
        return rpcResponse.getData();
    }
}
